const jsonServer = require("json-server");
const {
  ChildNodeExistError,
  NoChildNodeError,
  MenuUsedError,
  RouteNameExistsError,
} = require("../CustomError.class");
const server = jsonServer.create();
const router = jsonServer.router("./db.json");

/* 更新数据缓存 */
server.use((req, res, next) => {
  router.db.read();
  next();
});

/**
 * Permission
 * @typedef {object} Permission - 权限列表
 * @property {string} resource.required - 接口路径 - _path
 * @property {array<string>} methods.required - 请求方法[GET|POST|PUT|PATCH|DELETE]
 * @property {string} action.required - 动作 - enum:allow,deny
 */

/**
 * PermissionEdit
 * @typedef {object} PermissionEdit - 权限列表
 * @property {string} resource - 接口路径 - _path
 * @property {array<string>} methods - 请求方法[GET|POST|PUT|PATCH|DELETE]
 * @property {string} action - 动作 - enum:allow,deny
 */

/**
 * Menus
 * @typedef {object} Menus
 * @property {number} pid.required - 上级菜单
 * @property {string} name.required - 菜单名称
 * @property {string} icon.required - 图标
 * @property {string} type.required - 菜单类型 dir目录，menu菜单，button按钮 - enum:dir,menu,button
 * @property {Permission} permission.required - 权限
 * @property {string} componentPath.required - 组件路径 - _path
 * @property {string} routePath.required - 路由路径 - _path
 * @property {string} routeName.required - 路由名称
 * @property {number} sort.required - 排序
 * @property {string} display.required - 显示状态：0隐藏，1显示 - enum:0,1
 */

/**
 * MenusEdit
 * @typedef {object} MenusEdit
 * @property {number} pid - 上级菜单
 * @property {string} name - 菜单名称
 * @property {string} icon - 图标
 * @property {string} type - 菜单类型 dir目录，menu菜单，button按钮 - enum:dir,menu,button
 * @property {PermissionEdit} permission - 权限
 * @property {string} componentPath - 组件路径 - _path
 * @property {string} routePath - 路由路径 - _path
 * @property {string} routeName - 路由名称
 * @property {number} sort - 排序
 * @property {string} display - 显示状态：0隐藏，1显示 - enum:0,1
 */

/**
 * MenusDir 当type="dir"时
 * @typedef {object} MenusDir
 * @property {number} pid.required - 上级菜单
 * @property {string} name.required - 菜单名称
 * @property {string} icon.required - 图标
 * @property {string} type.required - 菜单类型 dir目录，menu菜单，button按钮 - enum:dir,menu,button
 * @property {string} routePath.required - 路由路径 - _path
 * @property {number} sort.required - 排序
 * @property {string} display.required - 显示状态：0隐藏，1显示 - enum:0,1
 */

/**
 * MenusMenu 当type="menu"时
 * @typedef {object} MenusMenu
 * @property {number} pid.required - 上级菜单
 * @property {string} name.required - 菜单名称
 * @property {string} icon.required - 图标
 * @property {string} type.required - 菜单类型 dir目录，menu菜单，button按钮 - enum:dir,menu,button
 * @property {Permission} permission.required - 权限
 * @property {string} componentPath.required - 组件路径 - _path
 * @property {string} routePath.required - 路由路径 - _path
 * @property {number} sort.required - 排序
 * @property {string} display.required - 显示状态：0隐藏，1显示 - enum:0,1
 */

/**
 * MenusButton 当type="button"时
 * @typedef {object} MenusButton
 * @property {number} pid.required - 上级菜单
 * @property {string} name.required - 菜单名称
 * @property {string} type.required - 菜单类型 dir目录，menu菜单，button按钮 - enum:dir,menu,button
 * @property {number} sort.required - 排序
 * @property {Permission} permission.required - 权限
 */

/* ------- */

/**
 * GET /menus
 * @tags menus - 菜单管理
 * @summary 获取菜单列表
 * @security auth
 * @param {string} name.query - 菜单名
 * @param {number} _page.query - 页码
 * @param {number} _limit.query - 每页条数
 * @param {string} _sort.query - 排序字段 - enum:sort,ctime,mtime
 * @param {string} _order.query - 排序规则 - enum:asc,desc
 * @return {array<Menus>} 200 - success response
 */
server.get("/menus", (req, res, next) => {
  next();
});

/**
 * GET /menus/tree
 * @tags menus
 * @summary 获取菜单（树形）列表
 * @security auth
 * @param {string} ids.query - 过滤字段逗号分隔(id1,id2,...)
 * @param {string} _sort.query - 排序字段 - enum:sort,ctime,mtime
 * @param {string} _order.query - 排序规则 - enum:asc,desc
 * @return {array<object>} 200 - success response
 */
server.get("/menus/tree", (req, res, next) => {
  const data = router.db.get("menus").value();
  const ids = req.query.ids?.split("-");

  function tree(data, pid = 0, r = []) {
    data.forEach((el) => {
      if (!el.children) el.children = [];
      if (el.pid === pid) {
        r.push(el);
        tree(data, el.id, el.children);
      }
    });
    return r;
  }
  res.json({
    status: 200,
    success: true,
    data: tree(ids ? data.filter((el) => ids.includes(String(el.id))) : data),
  });
});

/**
 * POST /menus
 * @tags menus - 菜单管理
 * @summary 添加菜单
 * @security auth
 * @param {string} name.query - 菜单名
 * @param {anyOf|MenusDir|MenusMenu|MenusButton } request.body.required
 * @return {Menus} 200 - success response
 */
server.post("/menus", (req, res, next) => {
  // 路由名重复验证
  if (router.db.get("menus").find({ routeName: req.body.routeName }).value())
    throw new RouteNameExistsError();

  if (+req.body.pid !== 0) {
    const r = router.db.get("menus").find({ id: +req.body.pid }).value();
    if (r.type === "button") {
      throw new NoChildNodeError();
    }
  }
  req.body = {
    pid: 0,
    name: "",
    type: "dir",
    sort: 0,
    ...req.body,
    icon: req.body.type === "button" ? "" : req.body.icon,
    permission: req.body.type === "dir" ? null : req.body.permission,
    componentPath: req.body.type === "menu" ? req.body.componentPath : "",
    routePath: req.body.type === "button" ? "" : req.body.routePath,
    display: req.body.type === "button" ? "" : req.body.display,
  };
  next();
});

/**
 * GET /menus/{id}
 * @tags menus
 * @summary 根据ID获取菜单
 * @security auth
 * @param {number} id.path.required - ID
 * @return {Menus} 200 - success response
 */
server.get("/menus", (req, res, next) => {
  next();
});

/**
 * PATCH /menus/{id}
 * @tags menus
 * @summary 编辑菜单
 * @security auth
 * @param {number} id.path.required - ID
 * @param {MenusEdit} request.body.required
 * @return {Menus} 200 - success response
 */

server.patch("/menus", (req, res, next) => {
  // 路由名重复验证
  if (router.db.get("menus").find({ routeName: req.body.routeName }).value())
    throw new RouteNameExistsError();

  next();
});

/**
 * DELETE /menus/{id}
 * @tags menus
 * @summary 根据ID删除菜单
 * @security auth
 * @param {number} id.path.required - ID
 * @return {Menus} 200 - success response
 */

server.delete("/menus/:id", (req, res, next) => {
  // 判断请先删除子节点
  const menu = router.db.get("menus").find({ pid: +req.params.id }).value();
  // 判断菜单已经被使用
  const rule = router.db
    .get("rules")
    .value()
    .find((el) => el.menus.includes(+req.params.id));

  if (rule) {
    throw new MenuUsedError("菜单已经被使用");
  }

  if (menu) {
    throw new ChildNodeExistError("请先删除子节点");
  }

  next();
});

module.exports = () => server;
